(function (global, factory) {
    if (typeof define === "function" && define.amd) {
        define(['module', 'exports'], factory);
    } else if (typeof exports !== "undefined") {
        factory(module, exports);
    } else {
        var mod = {
            exports: {}
        };
        factory(mod, mod.exports);
        global.WOW = mod.exports;
    }
})(this, function (module, exports) {
    'use strict';

    Object.defineProperty(exports, "__esModule", {
        value: true
    });

    var _class, _temp;

    function _classCallCheck(instance, Constructor) {
        if (!(instance instanceof Constructor)) {
            throw new TypeError("Cannot call a class as a function");
        }
    }

    var _createClass = function () {
        function defineProperties(target, props) {
            for (var i = 0; i < props.length; i++) {
                var descriptor = props[i];
                descriptor.enumerable = descriptor.enumerable || false;
                descriptor.configurable = true;
                if ("value" in descriptor) descriptor.writable = true;
                Object.defineProperty(target, descriptor.key, descriptor);
            }
        }

        return function (Constructor, protoProps, staticProps) {
            if (protoProps) defineProperties(Constructor.prototype, protoProps);
            if (staticProps) defineProperties(Constructor, staticProps);
            return Constructor;
        };
    }();

    function isIn(needle, haystack) {
        return haystack.indexOf(needle) >= 0;
    }

    function extend(custom, defaults) {
        for (var key in defaults) {
            if (custom[key] == null) {
                var value = defaults[key];
                custom[key] = value;
            }
        }
        return custom;
    }

    function isMobile(agent) {
        return (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(agent)
        );
    }

    function createEvent(event) {
        var bubble = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];
        var cancel = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2];
        var detail = arguments.length <= 3 || arguments[3] === undefined ? null : arguments[3];

        var customEvent = void 0;
        if (document.createEvent != null) {
            // W3C DOM
            customEvent = document.createEvent('CustomEvent');
            customEvent.initCustomEvent(event, bubble, cancel, detail);
        } else if (document.createEventObject != null) {
            // IE DOM < 9
            customEvent = document.createEventObject();
            customEvent.eventType = event;
        } else {
            customEvent.eventName = event;
        }

        return customEvent;
    }

    function emitEvent(elem, event) {
        if (elem.dispatchEvent != null) {
            // W3C DOM
            elem.dispatchEvent(event);
        } else if (event in (elem != null)) {
            elem[event]();
        } else if ('on' + event in (elem != null)) {
            elem['on' + event]();
        }
    }

    function addEvent(elem, event, fn) {
        if (elem.addEventListener != null) {
            // W3C DOM
            elem.addEventListener(event, fn, false);
        } else if (elem.attachEvent != null) {
            // IE DOM
            elem.attachEvent('on' + event, fn);
        } else {
            // fallback
            elem[event] = fn;
        }
    }

    function removeEvent(elem, event, fn) {
        if (elem.removeEventListener != null) {
            // W3C DOM
            elem.removeEventListener(event, fn, false);
        } else if (elem.detachEvent != null) {
            // IE DOM
            elem.detachEvent('on' + event, fn);
        } else {
            // fallback
            delete elem[event];
        }
    }

    function getInnerHeight() {
        if ('innerHeight' in window) {
            return window.innerHeight;
        }

        return document.documentElement.clientHeight;
    }

    // Minimalistic WeakMap shim, just in case.
    var WeakMap = window.WeakMap || window.MozWeakMap || function () {
        function WeakMap() {
            _classCallCheck(this, WeakMap);

            this.keys = [];
            this.values = [];
        }

        _createClass(WeakMap, [{
            key: 'get',
            value: function get(key) {
                for (var i = 0; i < this.keys.length; i++) {
                    var item = this.keys[i];
                    if (item === key) {
                        return this.values[i];
                    }
                }
                return undefined;
            }
        }, {
            key: 'set',
            value: function set(key, value) {
                for (var i = 0; i < this.keys.length; i++) {
                    var item = this.keys[i];
                    if (item === key) {
                        this.values[i] = value;
                        return this;
                    }
                }
                this.keys.push(key);
                this.values.push(value);
                return this;
            }
        }]);

        return WeakMap;
    }();

    // Dummy MutationObserver, to avoid raising exceptions.
    var MutationObserver = window.MutationObserver || window.WebkitMutationObserver || window.MozMutationObserver || (_temp = _class = function () {
        function MutationObserver() {
            _classCallCheck(this, MutationObserver);

            if (typeof console !== 'undefined' && console !== null) {
                console.warn('MutationObserver is not supported by your browser.');
                console.warn('WOW.js cannot detect dom mutations, please call .sync() after loading new content.');
            }
        }

        _createClass(MutationObserver, [{
            key: 'observe',
            value: function observe() { }
        }]);

        return MutationObserver;
    }(), _class.notSupported = true, _temp);

    // getComputedStyle shim, from http://stackoverflow.com/a/21797294
    var getComputedStyle = window.getComputedStyle || function getComputedStyle(el) {
        var getComputedStyleRX = /(\-([a-z]){1})/g;
        return {
            getPropertyValue: function getPropertyValue(prop) {
                if (prop === 'float') {
                    prop = 'styleFloat';
                }
                if (getComputedStyleRX.test(prop)) {
                    prop.replace(getComputedStyleRX, function (_, _char) {
                        return _char.toUpperCase();
                    });
                }
                var currentStyle = el.currentStyle;

                return (currentStyle != null ? currentStyle[prop] : void 0) || null;
            }
        };
    };

    var WOW = function () {
        function WOW() {
            var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];

            _classCallCheck(this, WOW);

            this.defaults = {
                boxClass: 'wow',
                animateClass: 'animated',
                offset: 0,
                mobile: true,
                live: true,
                callback: null,
                scrollContainer: null,
                resetAnimation: true
            };

            this.animate = function animateFactory() {
                if ('requestAnimationFrame' in window) {
                    return function (callback) {
                        return window.requestAnimationFrame(callback);
                    };
                }
                return function (callback) {
                    return callback();
                };
            }();

            this.vendors = ['moz', 'webkit'];

            this.start = this.start.bind(this);
            this.resetAnimation = this.resetAnimation.bind(this);
            this.scrollHandler = this.scrollHandler.bind(this);
            this.scrollCallback = this.scrollCallback.bind(this);
            this.scrolled = true;
            this.config = extend(options, this.defaults);
            if (options.scrollContainer != null) {
                this.config.scrollContainer = document.querySelector(options.scrollContainer);
            }
            // Map of elements to animation names:
            this.animationNameCache = new WeakMap();
            this.wowEvent = createEvent(this.config.boxClass);
        }

        _createClass(WOW, [{
            key: 'init',
            value: function init() {
                this.element = window.document.documentElement;
                if (isIn(document.readyState, ['interactive', 'complete'])) {
                    this.start();
                } else {
                    addEvent(document, 'DOMContentLoaded', this.start);
                }
                this.finished = [];
            }
        }, {
            key: 'start',
            value: function start() {
                var _this = this;

                this.stopped = false;
                this.boxes = [].slice.call(this.element.querySelectorAll('.' + this.config.boxClass));
                this.all = this.boxes.slice(0);
                if (this.boxes.length) {
                    if (this.disabled()) {
                        this.resetStyle();
                    } else {
                        for (var i = 0; i < this.boxes.length; i++) {
                            var box = this.boxes[i];
                            this.applyStyle(box, true);
                        }
                    }
                }
                if (!this.disabled()) {
                    addEvent(this.config.scrollContainer || window, 'scroll', this.scrollHandler);
                    addEvent(window, 'resize', this.scrollHandler);
                    this.interval = setInterval(this.scrollCallback, 50);
                }
                if (this.config.live) {
                    var mut = new MutationObserver(function (records) {
                        for (var j = 0; j < records.length; j++) {
                            var record = records[j];
                            for (var k = 0; k < record.addedNodes.length; k++) {
                                var node = record.addedNodes[k];
                                _this.doSync(node);
                            }
                        }
                        return undefined;
                    });
                    mut.observe(document.body, {
                        childList: true,
                        subtree: true
                    });
                }
            }
        }, {
            key: 'stop',
            value: function stop() {
                this.stopped = true;
                removeEvent(this.config.scrollContainer || window, 'scroll', this.scrollHandler);
                removeEvent(window, 'resize', this.scrollHandler);
                if (this.interval != null) {
                    clearInterval(this.interval);
                }
            }
        }, {
            key: 'sync',
            value: function sync() {
                if (MutationObserver.notSupported) {
                    this.doSync(this.element);
                }
            }
        }, {
            key: 'doSync',
            value: function doSync(element) {
                if (typeof element === 'undefined' || element === null) {
                    element = this.element;
                }
                if (element.nodeType !== 1) {
                    return;
                }
                element = element.parentNode || element;
                var iterable = element.querySelectorAll('.' + this.config.boxClass);
                for (var i = 0; i < iterable.length; i++) {
                    var box = iterable[i];
                    if (!isIn(box, this.all)) {
                        this.boxes.push(box);
                        this.all.push(box);
                        if (this.stopped || this.disabled()) {
                            this.resetStyle();
                        } else {
                            this.applyStyle(box, true);
                        }
                        this.scrolled = true;
                    }
                }
            }
        }, {
            key: 'show',
            value: function show(box) {
                this.applyStyle(box);
                box.className = box.className + ' ' + this.config.animateClass;
                if (this.config.callback != null) {
                    this.config.callback(box);
                }
                emitEvent(box, this.wowEvent);

                if (this.config.resetAnimation) {
                    addEvent(box, 'animationend', this.resetAnimation);
                    addEvent(box, 'oanimationend', this.resetAnimation);
                    addEvent(box, 'webkitAnimationEnd', this.resetAnimation);
                    addEvent(box, 'MSAnimationEnd', this.resetAnimation);
                }

                return box;
            }
        }, {
            key: 'applyStyle',
            value: function applyStyle(box, hidden) {
                var _this2 = this;

                var duration = box.getAttribute('data-wow-duration');
                var delay = box.getAttribute('data-wow-delay');
                var iteration = box.getAttribute('data-wow-iteration');

                return this.animate(function () {
                    return _this2.customStyle(box, hidden, duration, delay, iteration);
                });
            }
        }, {
            key: 'resetStyle',
            value: function resetStyle() {
                for (var i = 0; i < this.boxes.length; i++) {
                    var box = this.boxes[i];
                    box.style.visibility = 'visible';
                }
                return undefined;
            }
        }, {
            key: 'resetAnimation',
            value: function resetAnimation(event) {
                if (event.type.toLowerCase().indexOf('animationend') >= 0) {
                    var target = event.target || event.srcElement;
                    target.className = target.className.replace(this.config.animateClass, '').trim();
                }
            }
        }, {
            key: 'customStyle',
            value: function customStyle(box, hidden, duration, delay, iteration) {
                if (hidden) {
                    this.cacheAnimationName(box);
                }
                box.style.visibility = hidden ? 'hidden' : 'visible';

                if (duration) {
                    this.vendorSet(box.style, { animationDuration: duration });
                }
                if (delay) {
                    this.vendorSet(box.style, { animationDelay: delay });
                }
                if (iteration) {
                    this.vendorSet(box.style, { animationIterationCount: iteration });
                }
                this.vendorSet(box.style, { animationName: hidden ? 'none' : this.cachedAnimationName(box) });

                return box;
            }
        }, {
            key: 'vendorSet',
            value: function vendorSet(elem, properties) {
                for (var name in properties) {
                    if (properties.hasOwnProperty(name)) {
                        var value = properties[name];
                        elem['' + name] = value;
                        for (var i = 0; i < this.vendors.length; i++) {
                            var vendor = this.vendors[i];
                            elem['' + vendor + name.charAt(0).toUpperCase() + name.substr(1)] = value;
                        }
                    }
                }
            }
        }, {
            key: 'vendorCSS',
            value: function vendorCSS(elem, property) {
                var style = getComputedStyle(elem);
                var result = style.getPropertyCSSValue(property);
                for (var i = 0; i < this.vendors.length; i++) {
                    var vendor = this.vendors[i];
                    result = result || style.getPropertyCSSValue('-' + vendor + '-' + property);
                }
                return result;
            }
        }, {
            key: 'animationName',
            value: function animationName(box) {
                var aName = void 0;
                try {
                    aName = this.vendorCSS(box, 'animation-name').cssText;
                } catch (error) {
                    // Opera, fall back to plain property value
                    aName = getComputedStyle(box).getPropertyValue('animation-name');
                }

                if (aName === 'none') {
                    return ''; // SVG/Firefox, unable to get animation name?
                }

                return aName;
            }
        }, {
            key: 'cacheAnimationName',
            value: function cacheAnimationName(box) {
                // https://bugzilla.mozilla.org/show_bug.cgi?id=921834
                // box.dataset is not supported for SVG elements in Firefox
                return this.animationNameCache.set(box, this.animationName(box));
            }
        }, {
            key: 'cachedAnimationName',
            value: function cachedAnimationName(box) {
                return this.animationNameCache.get(box);
            }
        }, {
            key: 'scrollHandler',
            value: function scrollHandler() {
                this.scrolled = true;
            }
        }, {
            key: 'scrollCallback',
            value: function scrollCallback() {
                if (this.scrolled) {
                    this.scrolled = false;
                    var results = [];
                    for (var i = 0; i < this.boxes.length; i++) {
                        var box = this.boxes[i];
                        if (box) {
                            if (this.isVisible(box)) {
                                this.show(box);
                                continue;
                            }
                            results.push(box);
                        }
                    }
                    this.boxes = results;
                    if (!this.boxes.length && !this.config.live) {
                        this.stop();
                    }
                }
            }
        }, {
            key: 'offsetTop',
            value: function offsetTop(element) {
                // SVG elements don't have an offsetTop in Firefox.
                // This will use their nearest parent that has an offsetTop.
                // Also, using ('offsetTop' of element) causes an exception in Firefox.
                while (element.offsetTop === undefined) {
                    element = element.parentNode;
                }
                var top = element.offsetTop;
                while (element.offsetParent) {
                    element = element.offsetParent;
                    top += element.offsetTop;
                }
                return top;
            }
        }, {
            key: 'isVisible',
            value: function isVisible(box) {
                var offset = box.getAttribute('data-wow-offset') || this.config.offset;
                var viewTop = this.config.scrollContainer && this.config.scrollContainer.scrollTop || window.pageYOffset;
                var viewBottom = viewTop + Math.min(this.element.clientHeight, getInnerHeight()) - offset;
                var top = this.offsetTop(box);
                var bottom = top + box.clientHeight;

                return top <= viewBottom && bottom >= viewTop;
            }
        }, {
            key: 'disabled',
            value: function disabled() {
                return !this.config.mobile && isMobile(navigator.userAgent);
            }
        }]);

        return WOW;
    }();

    exports.default = WOW;
    module.exports = exports['default'];
});